/**
 * Copyright (C) 2009+ Spark Zheng
 *
 * @file        src/libdserver.c 
 * @date        2010-04-26
 * @brief	general_network
 * @see         dserver
 * @history
 */

#include <string.h>
#include <dlib/mempool.h>
#include <dlib/str.h>

#include "libdserver.h"
#include "dserver_errno.h"

/** global variable in prot_tcp.c */
extern prot_ops_t tcp_ops;
/** global variable in prot_udp.c */
extern prot_ops_t udp_ops;

struct dserver_handler {
	prot_node_t		*dh_prot_node;	/** protocol node */
	prot_node_t		*dh_monitor;	/** monitor node if needed */
};

/**
 * dserver_set_handlers
 * @brief set callbacks for current handler
 * @param handler: current handler
 * @param init_cb: session_init callback
 * @param worker_cb: session_worker callback
 * @param exit_cb: session_exit callback
 */
int dserver_set_handlers(dserver_handler_t *handler, dserver_session_init_fn init_cb,
		dserver_session_worker_fn worker_cb, dserver_session_exit_fn exit_cb)
{
	if (handler == NULL || worker_cb == NULL) {
		return -E_ARGS_ILLEGAL;
	}
	handler->dh_prot_node->s_ops.p_protocol_load = NULL;
	handler->dh_prot_node->s_ops.p_session_init = init_cb;
	handler->dh_prot_node->s_ops.p_session_worker = worker_cb;
	handler->dh_prot_node->s_ops.p_session_exit = exit_cb;
	handler->dh_prot_node->s_ops.p_protocol_unload = NULL;
	return RET_SUCCESS;
}

/**
 * dserver_tcpserver_init
 * @brief init a tcp server
 * @return tcp server handler or NULL on error
 * @param host: listened on host & port
 * @param port: listened on host & port
 * @param timeout: protocol timeout
 */
dserver_handler_t *dserver_tcpserver_init(const char *host, in_port_t port, uint16_t timeout)
{
	prot_node_t *protocol = NULL;
	dserver_handler_t *handler = NULL;

	handler = dmalloc_nb(sizeof(*handler));
	if (handler == NULL) {
		return NULL;
	}

	protocol = dmalloc_nb(sizeof(*protocol));
	if (protocol == NULL) {
		dfree(handler);
		return NULL;
	}
	handler->dh_prot_node = protocol;

	protocol->p_name = DSERVER_PROT_NAME;
	protocol->p_ops = tcp_ops;

	protocol->p_settings.p_type = PROTOCOL_TCP;
	protocol->p_settings.p_host = strdup(host);
	if (protocol->p_settings.p_host == NULL) {
		dfree(protocol);
		dfree(handler);
		return NULL;
	}
	protocol->p_settings.p_port = port;
	protocol->p_settings.p_timeout = timeout;
	protocol->p_settings.p_priority = DSERVER_PROT_PRIORITY;

	protocol->p_settings.p_tcp_nodelay = DSERVER_PROT_TCP_NODELAY;
	protocol->p_settings.p_tcp_keepalive = DSERVER_PROT_TCP_KEEPALIVE;
	protocol->p_settings.p_max_rx_buf = DSERVER_PROT_MAX_RX_BUF;
	protocol->p_settings.p_max_tx_buf = DSERVER_PROT_MAX_TX_BUF;

	protocol->p_sfd = -1;
	protocol->p_main_event = event_init();
	if (protocol->p_main_event == NULL) {
		dfree(protocol->p_settings.p_host);
		dfree(protocol);
		dfree(handler);
		return NULL;
	}

	protocol->p_base.p_tcp_main_cpool = dserver_cpool_init(DSERVER_PROT_MAXCONN);
	if (protocol->p_base.p_tcp_main_cpool == NULL) {
		event_base_free(protocol->p_main_event);
		dfree(protocol->p_settings.p_host);
		dfree(protocol);
		dfree(handler);
		return NULL;
	}

	protocol->p_data = NULL;

	return handler;
}

/**
 * dserver_tcpserver_exit
 * @brief destroy a tcp server
 * @param handler: tcp server handler
 */
void dserver_tcpserver_exit(dserver_handler_t *handler)
{
	prot_node_t *protocol;

	if (handler == NULL || handler->dh_prot_node == NULL) {
		return;
	}

	protocol = handler->dh_prot_node;
	
	if (protocol->p_base.p_tcp_main_cpool != NULL) {
		dserver_cpool_exit(protocol->p_base.p_tcp_main_cpool);
		protocol->p_base.p_tcp_main_cpool = NULL;
	}

	if (protocol->p_main_event != NULL) {
		event_base_free(protocol->p_main_event);
		protocol->p_main_event = NULL;
	}

	dfree(protocol->p_settings.p_host);
	protocol->p_settings.p_host = NULL;
	dfree(protocol);
	protocol = NULL;
	dfree(handler);
	handler = NULL;

	return;
}

/**
 * dserver_tcpserver_run
 * @brief running a tcp server forever
 * @return < 0 error
 * @param handler: tcp server handler
 */
int dserver_tcpserver_run(dserver_handler_t *handler)
{
	prot_node_t *protocol;

	if (handler == NULL || handler->dh_prot_node == NULL) {
		return -E_ARGS_ILLEGAL;
	}

	protocol = handler->dh_prot_node;

	if (protocol->p_ops.p_server_init != NULL) {
		protocol->p_sfd = (*protocol->p_ops.p_server_init)(protocol->p_settings.p_host,
				protocol->p_settings.p_port, protocol->p_settings.p_timeout);
		if (protocol->p_sfd < 0) {
			return -E_SOCKET;
		}
	} else {
		return -E_INPUT_ILLEGAL;
	}

	/** set tcp socket options */
	//(void)dserver_set_rcv_timeout(protocol->p_sfd, protocol->p_settings.p_timeout, 0);
	//(void)dserver_set_snd_timeout(protocol->p_sfd, protocol->p_settings.p_timeout, 0);
	if (protocol->p_settings.p_tcp_nodelay != 0) {
		(void)dserver_set_tcp_nodelay(protocol->p_sfd);
	}
	if (protocol->p_settings.p_tcp_keepalive != 0) {
		(void)dserver_set_tcp_keepalive(protocol->p_sfd);
	}
	/* TODO: rcv/snd buf */

	if (dserver_tcp_conn_receive(protocol) < 0) {
		return -E_EVENT;
	} 

	while (1) {
		(void)event_base_loop(protocol->p_main_event, 0);
	}
	/* never go here */
	
	//return RET_SUCCESS;
}

int dserver_tcpclient_init(const char *host, in_port_t port, uint16_t timeout)
{
	if (host == NULL || port == 0) {
		return -E_ARGS_ILLEGAL;
	}

	int sock =  dserver_tcp_client_new(host, port, timeout);
	if (sock > 0 && timeout > 0) {
		(void)dserver_set_rcv_timeout(sock, timeout, 0);
		(void)dserver_set_snd_timeout(sock, timeout, 0);
	}
	
	return sock;
}

int dserver_tcpclient_exit(int fd)
{
	return close(fd);
}

ssize_t dserver_tcpdata_write(int sockfd, const char *buf, size_t buf_len, uint16_t timeo)
{
	return dserver_data_write(sockfd, buf, buf_len, timeo);
}

ssize_t dserver_tcpdata_writev(int sockfd, const struct iovec *vec,
		uint16_t vec_nr, uint16_t timeo)
{
	return dserver_data_writev(sockfd, vec, vec_nr, timeo);
}

ssize_t dserver_tcpdata_read(int sockfd, char *buf, size_t buf_len, uint16_t timeo)
{
	return dserver_data_read(sockfd, buf, buf_len, timeo);
}

ssize_t dserver_tcpdata_readv(int sockfd, struct iovec *vec,
		uint16_t vec_nr, uint16_t timeo)
{
	return dserver_data_readv(sockfd, vec, vec_nr, timeo);
}

ssize_t dserver_tcpdata_sendfile(int infd, int outfd, size_t nr, off_t *from, off_t *to)
{
	return dserver_data_sendfile(infd, outfd, nr, from, to);
}

/**
 * dserver_tcpserver_init
 * @brief init a udp server
 * @return udp server handler or NULL on error
 * @param host: listened on host & port
 * @param port: listened on host & port
 * @param timeout: protocol timeout
 */
dserver_handler_t *dserver_udpserver_init(const char *host, in_port_t port, uint16_t timeout)
{
	prot_node_t *protocol = NULL;
	dserver_handler_t *handler = NULL;

	handler = dmalloc_nb(sizeof(*handler));
	if (handler == NULL) {
		return NULL;
	}

	protocol = dmalloc_nb(sizeof(*protocol));
	if (protocol == NULL) {
		dfree(handler);
		return NULL;
	}
	handler->dh_prot_node = protocol;

	protocol->p_name = DSERVER_PROT_NAME;
	protocol->p_ops = udp_ops;

	protocol->p_settings.p_type = PROTOCOL_UDP;
	protocol->p_settings.p_host = strdup(host);
	if (protocol->p_settings.p_host == NULL) {
		dfree(protocol);
		dfree(handler);
		return NULL;
	}
	protocol->p_settings.p_port = port;
	protocol->p_settings.p_timeout = timeout;
	protocol->p_settings.p_priority = DSERVER_PROT_PRIORITY;

	protocol->p_settings.p_tcp_nodelay = 0;
	protocol->p_settings.p_tcp_keepalive = 0;
	protocol->p_settings.p_max_rx_buf = DSERVER_PROT_MAX_RX_BUF;
	protocol->p_settings.p_max_tx_buf = DSERVER_PROT_MAX_TX_BUF;

	protocol->p_sfd = -1;
	protocol->p_main_event = event_init();
	if (protocol->p_main_event == NULL) {
		dfree(protocol->p_settings.p_host);
		dfree(protocol);
		dfree(handler);
		return NULL;
	}
	protocol->p_base.p_udp_main_conn = dmalloc_nb(sizeof(*protocol->p_base.p_udp_main_conn));
	if (protocol->p_base.p_udp_main_conn == NULL) {
		event_base_free(protocol->p_main_event);
		dfree(protocol->p_settings.p_host);
		dfree(protocol);
		dfree(handler);
		return NULL;
	}

	protocol->p_data = NULL;

	return handler;
}

/**
 * dserver_udpserver_exit
 * @brief destroy a tcp server
 * @return udp server handler or NULL on error
 * @param handler: udp server handler
 */
void dserver_udpserver_exit(dserver_handler_t *handler)
{
	prot_node_t *protocol;

	if (handler == NULL || handler->dh_prot_node == NULL) {
		return;
	}

	protocol = handler->dh_prot_node;
	
	if (protocol->p_base.p_udp_main_conn != NULL) {
		dfree(protocol->p_base.p_udp_main_conn);
		protocol->p_base.p_udp_main_conn = NULL;
	}

	if (protocol->p_main_event != NULL) {
		event_base_free(protocol->p_main_event);
		protocol->p_main_event = NULL;
	}

	dfree(protocol->p_settings.p_host);
	protocol->p_settings.p_host = NULL;
	dfree(protocol);
	protocol = NULL;
	dfree(handler);
	handler = NULL;

	return;
}

/**
 * dserver_udpserver_run
 * @brief running a udp server forever
 * @param handler: udp server handler
 */
int dserver_udpserver_run(dserver_handler_t *handler)
{
	prot_node_t *protocol;

	if (handler == NULL || handler->dh_prot_node == NULL) {
		return -E_ARGS_ILLEGAL;
	}

	protocol = handler->dh_prot_node;

	if (protocol->p_ops.p_server_init != NULL) {
		protocol->p_sfd = (*protocol->p_ops.p_server_init)(protocol->p_settings.p_host,
				protocol->p_settings.p_port, protocol->p_settings.p_timeout);
		if (protocol->p_sfd < 0) {
			return -E_SOCKET;
		}
	} else {
		return -E_INPUT_ILLEGAL;
	}

	/** set udp socket options */
	//(void)dserver_set_rcv_timeout(protocol->p_sfd, protocol->p_settings.p_timeout, 0);
	//(void)dserver_set_snd_timeout(protocol->p_sfd, protocol->p_settings.p_timeout, 0);
	/* TODO: rcv/snd buf */

	if (dserver_udp_data_receive(protocol) < 0) {
		return -E_EVENT;
	} 

	while (1) {
		(void)event_base_loop(protocol->p_main_event, 0);
	}
	/* never go here */

	//return RET_SUCCESS;
}

int dserver_udpclient_init(const char *host, in_port_t port, uint16_t timeout)
{
	int sock =  dserver_udp_client_new(host, port, timeout);
	if (sock > 0 && timeout > 0) {
		(void)dserver_set_rcv_timeout(sock, timeout, 0);
		(void)dserver_set_snd_timeout(sock, timeout, 0);
	}
	return sock;
}

int dserver_udpclient_exit(int fd)
{
	return close(fd);
}

ssize_t dserver_udpdata_write(int sockfd, const char *buf, size_t buf_len, uint16_t timeo)
{
	return dserver_data_write(sockfd, buf, buf_len, timeo);
}

ssize_t dserver_udpdata_writev(int sockfd, const struct iovec *vec,
		uint16_t vec_nr, uint16_t timeo)
{
	return dserver_data_writev(sockfd, vec, vec_nr, timeo);
}

ssize_t dserver_udpdata_read(int sockfd, char *buf, size_t buf_len, uint16_t timeo)
{
	return dserver_data_read(sockfd, buf, buf_len, timeo);
}

ssize_t dserver_udpdata_readv(int sockfd, struct iovec *vec,
		uint16_t vec_nr, uint16_t timeo)
{
	return dserver_data_readv(sockfd, vec, vec_nr, timeo);
}

ssize_t dserver_udpdata_sendfile(int infd, int outfd, size_t nr, off_t *from, off_t *to)
{
	return dserver_data_sendfile(infd, outfd, nr, from, to);
}


